package com.demo.crm.controller;

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.matchprocessor.ClassAnnotationMatchProcessor;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Entity;
import javax.transaction.Transactional;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.arvato.crm.annotation.CSVField;
import com.arvato.crm.annotation.EntityType;
import com.arvato.crm.annotation.EntityType.Type;
import com.arvato.crm.model.entity.ImportConfig;
import com.arvato.crm.model.entity.ImportMapping;
import com.arvato.crm.model.entity.ext.JacksonUtil;
import com.arvato.crm.service.CrudFactory;
import com.arvato.crm.service.CrudService;
import com.demo.crm.model.AjaxInfo;

@Controller
@RequestMapping("/entity")
@Slf4j
public class EntityController {

	final static List<Map<String, String>> IMPORT_ENTITY_LIST = new ArrayList<>();
	final static List<Map<String, Object>> CORE_ENTITY_LIST = new ArrayList<>();
	static {
		FastClasspathScanner scanner = new FastClasspathScanner("com.arvato.crm.model.entity.temp");
		scanner.matchClassesWithAnnotation(EntityType.class, new ClassAnnotationMatchProcessor() {
			@SuppressWarnings("serial")
			@Override
			public void processMatch(Class<?> matchingClass) {
				EntityType entityType = matchingClass.getAnnotation(EntityType.class);
				log.info("matchingClass is:{}", matchingClass);
				if (entityType.type().equals(Type.IMPORT_ENTITY)) {
					final String entityClass = matchingClass.getName();
					final String name = entityType.name();
					IMPORT_ENTITY_LIST.add(new HashMap<String, String>() {
						{
							put("name", name);
							put("class", entityClass);
						}
					});
				}
			}
		}).scan();
		//
		FastClasspathScanner coreScanner = new FastClasspathScanner("com.arvato.crm.model.entity");
		coreScanner.matchClassesWithAnnotation(Entity.class, new ClassAnnotationMatchProcessor() {
			@SuppressWarnings("serial")
			@Override
			public void processMatch(Class<?> matchingClass) {
				EntityType entityType = matchingClass.getAnnotation(EntityType.class);
				log.info("matchingClass is:{}", matchingClass);
				if (entityType == null || !entityType.type().equals(Type.IMPORT_ENTITY)) {
					final String entityClass = matchingClass.getName();
					CORE_ENTITY_LIST.add(new HashMap<String, Object>() {
						{
							put("class", entityClass);
						}
					});
				}
			}
		}).scan();
	}

	@RequestMapping("/getImportEntitys")
	@ResponseBody
	public List<Map<String, String>> getImportEntitys() {
		return IMPORT_ENTITY_LIST;
	}

	@SuppressWarnings({ "serial", "unchecked" })
	@RequestMapping("/getEntityFields")
	@ResponseBody
	public List<Map<String, Object>> getEntityFields(String entityClassName, String configId) {
		Class<?> entityClass = null;
		try {
			entityClass = Class.forName(entityClassName);
		} catch (Exception e) {
			log.error("entityClass:" + entityClassName + " not found", e);
			throw new RuntimeException("entityClass:" + entityClassName + " not found");
		}
		CrudService service = CrudFactory.create("import_config");
		ImportConfig config = null;
		if (configId != null) {
			config = service.findById(configId);
		}
		List<Field> fields = FieldUtils.getAllFieldsList(entityClass);
		List<Map<String, Object>> entityFields = new ArrayList<>();
		for (final Field field : fields) {
			final String fieldName = field.getName();
			final Class<?> fieldType = field.getType();
			if (!"serialVersionUID".equals(fieldName)) {
				List<ImportMapping> mappings = config != null ? config.getMappings() : Collections.EMPTY_LIST;
				mappings = mappings == null ? Collections.EMPTY_LIST : mappings;
				final ImportMapping matchedMapping = (ImportMapping) CollectionUtils.find(mappings, new Predicate() {
					@Override
					public boolean evaluate(Object mapping) {
						return fieldName.equals(((ImportMapping) mapping).getFieldName());
					}
				});
				entityFields.add(new HashMap<String, Object>() {
					{
						put("fieldName", fieldName);
						put("fieldType", fieldType.getSimpleName());
						put("comment", fieldName);
						if(field.isAnnotationPresent(CSVField.class)){
							CSVField csvField = field.getAnnotation(CSVField.class);
							put("comment",csvField.comment());
						}
						if (matchedMapping != null) {
							put("mappingFieldName", matchedMapping.getMappingFieldName());
							put("mappingFieldType", matchedMapping.getMappingFieldType());
							put("mappingConfig", matchedMapping.getMappingConfig());
						}
					}
				});
			}
		}
		return entityFields;
	}

	@RequestMapping("/getCoreEntitys")
	@ResponseBody
	public List<Map<String, Object>> getCoreEntitys() {
		return CORE_ENTITY_LIST;
	}

	@SuppressWarnings("unchecked")
	@RequestMapping("/saveImportMapping")
	@ResponseBody
	@Transactional
	public AjaxInfo saveImportMapping(String configId, String mappingEntityClass, String preTasks, String mappings) {
		CrudService service = CrudFactory.create(ImportConfig.class);
		ImportConfig config = service.findById(configId);
		config.setPreTasks(preTasks);
		config.setMappingEntityClass(mappingEntityClass);
		config.setMappings(JacksonUtil.fromString(mappings, List.class, ImportMapping.class));
		service.save(config);
		return AjaxInfo.SUCCESS;
	}

}
